﻿// Copyright 2006 - Ricardo Stuven (rstuven@gmail.com)
//
// This file is part of MsSqlSpatial.
// MsSqlSpatial is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// MsSqlSpatial is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public License
// along with MsSqlSpatial; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 

using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using GisSharpBlog.NetTopologySuite.Geometries;
using GeoAPI.Geometries;
using System.IO;
using MsSqlSpatial;
using System.Collections.Generic;
using Aggregate = MsSqlSpatial.Aggregate<System.Collections.Generic.List<GeoAPI.Geometries.IGeometry>>;


/// <summary>
/// This aggregate function returns a GEOMETRYCOLLECTION or a MULTI object from a set of geometries.
/// </summary>
/// <example>
/// For example,
/// <code lang="SQL">
/// SELECT ST.Collect(GEOM) FROM GEOMTABLE GROUP BY ATTRCOLUMN
/// </code>
/// will return a separate GEOMETRYCOLLECTION for each distinct value of ATTRCOLUMN.
/// </example>
[Serializable]
[SqlUserDefinedAggregate(Format.UserDefined
	, IsInvariantToNulls=true
	, IsInvariantToDuplicates=false
	, IsInvariantToOrder=false
	, IsNullIfEmpty=false
	, MaxByteSize=8000
	)]
public struct CollectAggregate : IBinarySerialize
{
	[NonSerialized]
	private string cacheKey;
	[NonSerialized]
	private List<IGeometry> aggregate;

	public void Init()
	{
		Aggregate.Initialize(out cacheKey, out this.aggregate);
	}

	public void Accumulate(SqlBytes ewkb)
	{
		IGeometry geometry = ConvertGeometry.ToGeometry(ewkb);
		if (geometry != null)
		{
			this.aggregate.Add(geometry);
		}
	}

	public void Merge(CollectAggregate Group)
	{
		if (Group.aggregate != null)
		{
			this.aggregate.AddRange(Group.aggregate);
		}
	}

	public SqlBytes Terminate()
	{
		List<IGeometry> aggregate = Aggregate.Terminate(cacheKey);
		if (aggregate.Count > 0)
		{
			// Take the SRID of the first geometry
			int srid = aggregate[0].SRID;
			return ConvertGeometry.ToSqlBytes(new GeometryFactory().CreateGeometryCollection(aggregate.ToArray()), srid);
		}
		else
		{
			return SqlBytes.Null;
		}
	}

	#region IBinarySerialize Members

	public void Read(BinaryReader r)
	{
		Aggregate.Read(r, out this.cacheKey, out this.aggregate);
	}

	public void Write(BinaryWriter w)
	{
		Aggregate.Write(w, cacheKey, aggregate);
	}

	#endregion

}
